package com.benshou.bcss.common.quartz;

import com.benshou.bcss.common.j17common.Means;
import com.benshou.bcss.common.j17common.Requires;
import com.benshou.bcss.common.quartz.bean.BeanQuartzJobRegister;
import com.benshou.bcss.common.trace.TraceIdContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.util.Optional;
import java.util.stream.Stream;

/**FIXME 还需要任务运行异常管理类
 * Quartz 任务运行器
 * <p>
 *     用于 Quartz 任务的实际执行。接收任务参数调用，并进行相应的定时任务调用。
 * </p>
 *
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class QuartzJobRunner implements Job {

    /**
     * Spring Bean 工厂实例
     */
    private final BeanFactory beanFactory;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // TraceId 用于链路追踪
        var traceId = TraceIdContext.get();
        var finalTraceId = traceId == null ? TraceIdContext.getOrInitialize() : traceId;
        try {
            logger.debug("TraceId = {}", finalTraceId);

            var dataMap = context.getJobDetail().getJobDataMap();
            // 目前仅处理 Bean 内注解声明的定时任务
            // 获取对应的定时任务方法
            var beanName = Requires.notnull(
                    dataMap.getString(BeanQuartzJobRegister.KEY_BEAN_NAME),
                    () -> new JobExecutionException("Bean name not found: " + dataMap));

            var bean = Requires.notnull(
                    this.beanFactory.getBean(beanName),
                    () -> new JobExecutionException("Bean not exists: " + beanName));

            var methodName = Requires.notnull(
                    dataMap.getString(BeanQuartzJobRegister.KEY_METHOD_NAME),
                    () -> new JobExecutionException("Method name not found: " + dataMap));

            var parameterTypes = Optional.ofNullable(dataMap.getString(BeanQuartzJobRegister.KEY_PARAMETER_TYPE_NAMES))
                    .filter(names -> !names.isBlank())
                    .map(names -> Stream.of(names.split(","))
                            .map(name -> {
                                try {
                                    return Class.forName(name);
                                } catch (ClassNotFoundException e) {
                                    throw new RuntimeException(e);
                                }
                            })
                            .toArray(Class<?>[]::new))
                    .orElseGet(Means::lenientEmptyArray);
            var method = Requires.notnull(
                    ReflectionUtils.findMethod(
                            AopUtils.getTargetClass(bean),
                            methodName,
                            parameterTypes),
                    () -> new JobExecutionException(
                            "Method not exists: beanName = " + beanName +
                                    ", class" + AopUtils.getTargetClass(bean) +
                                    ", method = " + methodName +
                                    ", parameterTypes = " +
                                    dataMap.getString(BeanQuartzJobRegister.KEY_PARAMETER_TYPE_NAMES)));

            // 执行方法 FIXME 处理带参数方法的调用
            method.invoke(bean);
        } catch (Exception e) {
            throw new JobExecutionException(e);
        } finally {
            TraceIdContext.set(traceId);
        }
    }
}
